home *** CD-ROM | disk | FTP | other *** search
- /*
- * File: fstruct.r
- * Contents: delete, get, key, insert, list, member, pop, pull, push, put,
- * set, table
- */
-
- "delete(x1,x2) - delete element x2 from set or table x1 if it is there"
- " (always succeeds and returns x1)."
-
- function{1} delete(s,x)
- abstract {
- return type(s) ** (set ++ table)
- }
-
- /*
- * The technique and philosophy here are the same
- * as used in insert - see comment there.
- */
- type_case s of {
- set:
- body {
- register uword hn;
- register union block **pd;
- union block *bp; /* does not need to be tended. */
- int res;
-
- hn = hash(&x);
-
- pd = memb(BlkLoc(s), &x, hn, &res);
- if (res == 1) {
- /*
- * The element is there so delete it.
- */
- *pd = (*pd)->selem.clink;
- (BlkLoc(s)->set.size)--;
- }
- return s;
- }
- table:
- body {
- union block *bp; /* does not need to be tended. */
- register union block **pd;
- register uword hn;
- int res;
-
- hn = hash(&x);
- pd = memb(BlkLoc(s), &x, hn, &res);
- if (res == 1) {
- /*
- * The element is there so delete it.
- */
- *pd = (*pd)->selem.clink;
- (BlkLoc(s)->set.size)--;
- }
- return s;
- }
- default:
- runerr(122, s)
- }
- end
-
-
- /*
- * c_get - convenient C-level access to the get function
- * returns 0 on failure, otherwise fills in res
- */
- int c_get(hp,res)
- struct b_list *hp;
- struct descrip *res;
- {
- register word i;
- register struct b_lelem *bp;
-
- /*
- * Fail if the list is empty.
- */
- if (hp->size <= 0)
- return 0;
-
- /*
- * Point bp at the first list block. If the first block has no
- * elements in use, point bp at the next list block.
- */
- bp = (struct b_lelem *) hp->listhead;
- if (bp->nused <= 0) {
- bp = (struct b_lelem *) bp->listnext;
- hp->listhead = (union block *) bp;
- bp->listprev = NULL;
- }
-
- /*
- * Locate first element and assign it to result for return.
- */
- i = bp->first;
- *res = bp->lslots[i];
-
- /*
- * Set bp->first to new first element, or 0 if the block is now
- * empty. Decrement the usage count for the block and the size
- * of the list.
- */
- if (++i >= bp->nslots)
- i = 0;
- bp->first = i;
- bp->nused--;
- hp->size--;
-
- return 1;
- }
-
- #begdef GetOrPop(get_or_pop)
- #get_or_pop "(x) - " #get_or_pop " an element from the left end of list x."
- /*
- * get(L) - get an element from end of list L.
- * Identical to pop(L).
- */
- function{0,1} get_or_pop(x)
- if !is:list(x) then
- runerr(108, x)
-
- abstract {
- return store[type(x).lst_elem]
- }
-
- body {
- if (!c_get((struct b_list *)BlkLoc(x),&result)) fail;
- return result;
- }
- end
- #enddef
-
- GetOrPop(get) /* get(x) - get an element from the left end of list x. */
- GetOrPop(pop) /* pop(x) - pop an element from the left end of list x. */
-
-
- "key(T) - generate successive keys (entry values) from table T."
-
- function{*} key(t)
- if !is:table(t) then
- runerr(124, t)
-
- abstract {
- return store[type(t).key]
- }
-
- inline {
- tended union block *ep;
- struct hgstate state;
-
- for (ep = hgfirst(BlkLoc(t), &state); ep != 0;
- ep = hgnext(BlkLoc(t), &state, ep))
- suspend ep->telem.tref;
- fail;
- }
- end
-
-
- "insert(x1,x2,x3) - insert element x2 into set or table x1 if not already there"
- " if x1 is a table, the assigned value for element x2 is x3."
- " (always succeeds and returns x1)."
-
- function{1} insert(s,x,y)
- type_case s of {
-
- set: {
- abstract {
- store[type(s).set_elem] = type(x)
- return type(s)
- }
-
- body {
- tended union block *bp, *bp2;
- register uword hn;
- int res;
- struct b_selem *se;
- register union block **pd;
-
- bp = BlkLoc(s);
- hn = hash(&x);
- /*
- * If x is a member of set s then res will have the value 1,
- * and pd will have a pointer to the pointer
- * that points to that member.
- * If x is not a member of the set then res will have
- * the value 0 and pd will point to the pointer
- * which should point to the member - thus we know where
- * to link in the new element without having to do any
- * repetitive looking.
- */
-
- /* get this now because can't tend pd */
- Protect(se = alcselem(&x,hn), runerr(0));
-
- pd = memb(bp, &x, hn, &res);
- if (res == 0) {
- /*
- * The element is not in the set - insert it.
- */
- addmem((struct b_set *)bp, se, pd);
- if (TooCrowded(bp))
- hgrow(bp);
- }
- else
- deallocate((union block *)se);
- return s;
- }
- }
-
- table: {
- abstract {
- store[type(s).key] = type(x)
- store[type(s).tbl_elem] = type(y)
- return type(s)
- }
-
- body {
- tended union block *bp, *bp2;
- union block **pd;
- struct b_telem *te;
- register uword hn;
- int res;
-
- bp = BlkLoc(s);
- hn = hash(&x);
-
- /* get this now because can't tend pd */
- Protect(te = alctelem(), runerr(0));
-
- pd = memb(bp, &x, hn, &res); /* search table for key */
- if (res == 0) {
- /*
- * The element is not in the table - insert it.
- */
- bp->table.size++;
- te->clink = *pd;
- *pd = (union block *)te;
- te->hashnum = hn;
- te->tref = x;
- te->tval = y;
- if (TooCrowded(bp))
- hgrow(bp);
- }
- else {
- /*
- * We found an existing entry; just change its value.
- */
- deallocate((union block *)te);
- te = (struct b_telem *) *pd;
- te->tval = y;
- }
- return s;
- }
- }
-
- default:
- runerr(122, s);
- }
- end
-
-
- "list(i,x) - create a list of size i, with initial value x."
-
- function{1} list(n,x)
- if !def:C_integer(n,0L) then
- runerr(101, n)
-
- abstract {
- return new list(type(x))
- }
-
- body {
- tended struct b_list *hp;
- register word i, size;
- word nslots;
- register struct b_lelem *bp; /* does not need to be tended */
-
- nslots = size = n;
-
- /*
- * Ensure that the size is positive and that the list-element block
- * has at least MinListSlots slots.
- */
- if (size < 0) {
- irunerr(205, n);
- errorfail;
- }
- if (nslots == 0)
- nslots = MinListSlots;
-
- /*
- * Allocate the list-header block and a list-element block.
- * Note that nslots is the number of slots in the list-element
- * block while size is the number of elements in the list.
- */
- Protect(hp = alclist(size), runerr(0));
- Protect(bp = alclstb(nslots, (word)0, size), runerr(0));
- hp->listhead = hp->listtail = (union block *) bp;
-
- /*
- * Initialize each slot.
- */
- for (i = 0; i < size; i++)
- bp->lslots[i] = x;
-
- /*
- * Return the new list.
- */
- return list(hp);
- }
- end
-
-
- "member(x1,x2) - returns x1 if x2 is a member of set or table x2 but fails"
- " otherwise."
-
- function{0,1} member(s,x)
- type_case s of {
-
- set: {
- abstract {
- return type(x) ** store[type(s).set_elem]
- }
- inline {
- int res;
- register uword hn;
-
- hn = hash(&x);
- memb(BlkLoc(s),&x,hn,&res);
- if (res==1)
- return x;
- else
- fail;
- }
- }
- table: {
- abstract {
- return type(x) ** store[type(s).key]
- }
- inline {
- int res;
- register uword hn;
-
- hn = hash(&x);
- memb(BlkLoc(s),&x,hn,&res);
- if (res == 1)
- return x;
- else
- fail;
- }
- }
- default:
- runerr(122, s)
- }
- end
-
-
- "pull(L) - pull an element from end of list L."
-
- function{0,1} pull(x)
- /*
- * x must be a list.
- */
- if !is:list(x) then
- runerr(108, x)
- abstract {
- return store[type(x).lst_elem]
- }
-
- body {
- register word i;
- register struct b_list *hp;
- register struct b_lelem *bp;
-
- /*
- * Point at list header block and fail if the list is empty.
- */
- hp = (struct b_list *) BlkLoc(x);
- if (hp->size <= 0)
- fail;
-
- /*
- * Point bp at the last list element block. If the last block has no
- * elements in use, point bp at the previous list element block.
- */
- bp = (struct b_lelem *) hp->listtail;
- if (bp->nused <= 0) {
- bp = (struct b_lelem *) bp->listprev;
- hp->listtail = (union block *) bp;
- bp->listnext = NULL;
- }
-
- /*
- * Set i to position of last element and assign the element to
- * result for return. Decrement the usage count for the block
- * and the size of the list.
- */
- i = bp->first + bp->nused - 1;
- if (i >= bp->nslots)
- i -= bp->nslots;
- result = bp->lslots[i];
- bp->nused--;
- hp->size--;
- return result;
- }
- end
-
-
-
- "push(L,x) - push x onto beginning of list L."
-
- function{1} push(x,val)
- /*
- * x must be a list.
- */
- if !is:list(x) then
- runerr(108, x)
- abstract {
- store[type(x).lst_elem] = type(val)
- return type(x)
- }
-
- body {
- tended struct b_list *hp;
- register word i;
- register struct b_lelem *bp; /* does not need to be tended */
- static two = 2; /* some compilers generat bad code for
- division by a constant that's a power of 2*/
- /*
- * Point hp at the list-header block and bp at the first
- * list-element block.
- */
- hp = (struct b_list *) BlkLoc(x);
- bp = (struct b_lelem *) hp->listhead;
-
- #ifdef EventMon /* initialize i so it's 0 if first list-element */
- i = 0; /* block isn't full */
- #endif /* EventMon */
-
- /*
- * If the first list-element block is full, allocate a new
- * list-element block, make it the first list-element block,
- * and make it the previous block of the former first list-element
- * block.
- */
- if (bp->nused >= bp->nslots) {
- /*
- * Set i to the size of block to allocate.
- */
- i = hp->size / two;
- if (i < MinListSlots)
- i = MinListSlots;
- #ifdef MaxListSlots
- if (i > MaxListSlots)
- i = MaxListSlots;
- #endif /* MaxListSlots */
-
- /*
- * Allocate a new list element block. If the block can't
- * be allocated, try smaller blocks.
- */
- while ((bp = alclstb(i, (word)0, (word)0)) == NULL) {
- i /= 4;
- if (i < MinListSlots)
- runerr(0);
- }
-
- hp->listhead->lelem.listprev = (union block *) bp;
- bp->listnext = hp->listhead;
- hp->listhead = (union block *) bp;
- }
-
- /*
- * Set i to position of new first element and assign val to
- * that element.
- */
- i = bp->first - 1;
- if (i < 0)
- i = bp->nslots - 1;
- bp->lslots[i] = val;
- /*
- * Adjust value of location of first element, block usage count,
- * and current list size.
- */
- bp->first = i;
- bp->nused++;
- hp->size++;
- /*
- * Return the list.
- */
- return x;
- }
- end
-
-
-
- "put(L,x) - put x onto end of list L."
-
- function{1} put(x,val)
- /*
- * x must be a list.
- */
- if !is:list(x) then
- runerr(108, x)
- abstract {
- store[type(x).lst_elem] = type(val)
- return type(x)
- }
-
- body {
- tended struct b_list *hp;
- register word i;
- register struct b_lelem *bp; /* does not need to be tended */
- static two = 2; /* some compilers generate bad code for
- division by a constant that's a power of 2*/
- /*
- * Point hp at the list-header block and bp at the last
- * list-element block.
- */
- hp = (struct b_list *)BlkLoc(x);
- bp = (struct b_lelem *) hp->listtail;
-
- #ifdef EventMon /* initialize i so it's 0 if last list-element */
- i = 0; /* block isn't full */
- #endif /* EventMon */
-
- /*
- * If the last list-element block is full, allocate a new
- * list-element block, make it the last list-element block,
- * and make it the next block of the former last list-element
- * block.
- */
- if (bp->nused >= bp->nslots) {
- /*
- * Set i to the size of block to allocate.
- */
- i = hp->size / two;
- if (i < MinListSlots)
- i = MinListSlots;
- #ifdef MaxListSlots
- if (i > MaxListSlots)
- i = MaxListSlots;
- #endif /* MaxListSlots */
- /*
- * Allocate a new list element block. If the block can't
- * be allocated, try smaller blocks.
- */
- while ((bp = alclstb(i, (word)0, (word)0)) == NULL) {
- i /= 4;
- if (i < MinListSlots)
- runerr(0);
- }
-
- hp->listtail->lelem.listnext = (union block *) bp;
- bp->listprev = hp->listtail;
- hp->listtail = (union block *) bp;
- }
-
- /*
- * Set i to position of new last element and assign val to
- * that element.
- */
- i = bp->first + bp->nused;
- if (i >= bp->nslots)
- i -= bp->nslots;
- bp->lslots[i] = val;
-
- /*
- * Adjust block usage count and current list size.
- */
- bp->nused++;
- hp->size++;
- /*
- * Return the list.
- */
- return x;
- }
- end
-
-
- "set(L) - create a set with members in list L."
- " The members are linked into hash chains which are"
- " arranged in increasing order by hash number."
-
- function{1} set(l)
-
- type_case l of {
- null: {
- abstract {
- return new set(empty_type)
- }
- inline {
- register union block * ps;
- ps = hmake(T_Set, (word)0, (word)0);
- if (ps == NULL)
- runerr(0);
- return set(ps);
- }
- }
-
- list: {
- abstract {
- return new set(store[type(l).lst_elem])
- }
-
- body {
- tended union block *pb;
- register uword hn;
- dptr pd;
- struct b_selem *ne; /* does not need to be tended */
- int res;
- word i, j;
- tended union block *ps;
- union block **pe;
-
- /*
- * Make a set of the appropriate size.
- */
- pb = BlkLoc(l);
- ps = hmake(T_Set, (word)0, pb->list.size);
- if (ps == NULL)
- runerr(0);
-
- /*
- * Chain through each list block and for
- * each element contained in the block
- * insert the element into the set if not there.
- *
- * ne always has a new element ready for use. We must get one
- * in advance, and stay one ahead, because pe can't be tended.
- */
- Protect(ne = alcselem(&nulldesc, (uword)0), runerr(0));
-
- for (pb = pb->list.listhead; pb != NULL; pb = pb->lelem.listnext) {
- for (i = 0; i < pb->lelem.nused; i++) {
- j = pb->lelem.first + i;
- if (j >= pb->lelem.nslots)
- j -= pb->lelem.nslots;
- pd = &pb->lelem.lslots[j];
- pe = memb(ps, pd, hn = hash(pd), &res);
- if (res == 0) {
- ne->setmem = *pd; /* add new element */
- ne->hashnum = hn;
- addmem((struct b_set *)ps, ne, pe);
- /* get another blk */
- Protect(ne = alcselem(&nulldesc, (uword)0), runerr(0));
- }
- }
- }
- deallocate((union block *)ne);
- return set(ps);
- }
- }
-
- default :
- runerr(108, l)
- }
- end
-
-
- "table(x) - create a table with default value x."
-
- function{1} table(x)
- abstract {
- return new table(empty_type, empty_type, type(x))
- }
- inline {
- union block *bp;
-
- bp = hmake(T_Table, (word)0, (word)0);
- if (bp == NULL)
- runerr(0);
- bp->table.defvalue = x;
- return table(bp);
- }
- end
-